<?php

namespace Dongasai\WkProtoPhp;

use Dongasai\WkProtoPhp\Enums\FrameType;

/**
 * 帧基础结构
 */
class Framer implements FrameInterface
{
    private int $frameType;
    private int $remainingLength = 0;
    private bool $noPersist = false;
    private bool $redDot = false;
    private bool $syncOnce = false;
    private bool $dup = false;
    private bool $hasServerVersion = false;
    private bool $end = false;
    private int $frameSize = 0;

    public function __construct(int $frameType = FrameType::UNKNOWN->value)
    {
        $this->frameType = $frameType;
    }

    /**
     * 从uint8创建Framer
     */
    public static function fromUint8(int $value): self
    {
        $framer = new self();
        $framer->noPersist = ($value & 0x01) > 0;
        $framer->redDot = ($value >> 1 & 0x01) > 0;
        $framer->syncOnce = ($value >> 2 & 0x01) > 0;
        $framer->dup = ($value >> 3 & 0x01) > 0;
        $framer->frameType = ($value >> 4) & 0x0F;

        // 特殊处理CONNACK类型
        if ($framer->frameType === FrameType::CONNACK->value) {
            $framer->hasServerVersion = ($value & 0x01) > 0;
        }

        return $framer;
    }

    /**
     * 转换为固定头部uint8
     */
    public static function toFixHeaderUint8(FrameInterface $frame): int
    {
        $typeAndFlags = self::encodeBool($frame->getDUP()) << 3 |
                       self::encodeBool($frame->getSyncOnce()) << 2 |
                       self::encodeBool($frame->getRedDot()) << 1 |
                       self::encodeBool($frame->getNoPersist());

        if ($frame->getFrameType() === FrameType::CONNACK) {
            $typeAndFlags = self::encodeBool($frame->getHasServerVersion());
        }

        return ($frame->getFrameType() << 4) | $typeAndFlags;
    }

    private static function encodeBool(bool $value): int
    {
        return $value ? 1 : 0;
    }

    // Getters and Setters
    public function getFrameType(): int
    {
        return $this->frameType;
    }

    public function setFrameType(int $frameType): void
    {
        $this->frameType = $frameType;
    }

    public function getRemainingLength(): int
    {
        return $this->remainingLength;
    }

    public function setRemainingLength(int $remainingLength): void
    {
        $this->remainingLength = $remainingLength;
    }

    public function getNoPersist(): bool
    {
        return $this->noPersist;
    }

    public function setNoPersist(bool $noPersist): void
    {
        $this->noPersist = $noPersist;
    }

    public function getRedDot(): bool
    {
        return $this->redDot;
    }

    public function setRedDot(bool $redDot): void
    {
        $this->redDot = $redDot;
    }

    public function getSyncOnce(): bool
    {
        return $this->syncOnce;
    }

    public function setSyncOnce(bool $syncOnce): void
    {
        $this->syncOnce = $syncOnce;
    }

    public function getDUP(): bool
    {
        return $this->dup;
    }

    public function setDUP(bool $dup): void
    {
        $this->dup = $dup;
    }

    public function getHasServerVersion(): bool
    {
        return $this->hasServerVersion;
    }

    public function setHasServerVersion(bool $hasServerVersion): void
    {
        $this->hasServerVersion = $hasServerVersion;
    }

    public function getEnd(): bool
    {
        return $this->end;
    }

    public function setEnd(bool $end): void
    {
        $this->end = $end;
    }

    public function getFrameSize(): int
    {
        return $this->frameSize;
    }

    public function setFrameSize(int $frameSize): void
    {
        $this->frameSize = $frameSize;
    }

    /**
     * 字符串表示
     */
    public function __toString(): string
    {
        return sprintf(
            'packetType: %s remainingLength: %d NoPersist: %v redDot: %v syncOnce: %v DUP: %v',
            FrameType::toString($this->frameType),
            $this->remainingLength,
            $this->noPersist,
            $this->redDot,
            $this->syncOnce,
            $this->dup
        );
    }
}